﻿using System.Net;
using System.Text;
using System.Text.RegularExpressions;

using Tszy.Unlimited.Base.Json;

namespace Tszy.Unlimited.Base.Extensions;

/// <summary>
/// 字符串扩展信息。
/// </summary>
public static class StringExtensions
{
    ///// <summary>
    ///// 验证 IP 的正则表达式。
    ///// </summary>
    //private static readonly Regex _ipExpression = new Regex(@"(((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)((\:\d{0,5}){0,1}))");

    /// <summary>
    /// 当前字符串是 null 还是 System.String.Empty 字符串。
    /// </summary>
    /// <param name="target">当前字符串。</param>
    /// <returns>是否为空。</returns>
    public static bool IsNullOrEmpty(this string target) => string.IsNullOrEmpty(target);

    /// <summary>
    /// 指示指定的字符串是 null、空还是仅由空白字符组成。
    /// </summary>
    /// <param name="target">当前字符串。</param>
    /// <returns>如果 value 参数为 null 或 System.String.Empty，或者如果 value 仅由空白字符组成，则为 true。</returns>
    public static bool IsNullOrWhiteSpace(this string target) => string.IsNullOrWhiteSpace(target);

    /// <summary>
    /// 判断对象是否为 System.Int32 类型的数字。
    /// </summary>
    /// <param name="target">当前字符串。</param>
    /// <returns>如果 target 参数为 System.Int32 类型的数字，则为 true。</returns>
    public static bool IsNumeric(this string target)
    {
        if (string.IsNullOrWhiteSpace(target)) return false;

        return target.Length > 0 && Regex.IsMatch(target, @"^[-]?[0-9]*[.]?[0-9]*$");
    }

    /// <summary>
    /// 判断是不是汉字。
    /// </summary>
    /// <param name="target"></param>
    /// <returns></returns>
    public static bool IsChinese(this string target)
    {
        if (string.IsNullOrWhiteSpace(target)) return false;

        foreach (var c in target)
        {
            if (!IsChinese(c))
            {
                return false;
            }
        }

        return true;
    }

    /// <summary>
    /// 判断是不是汉字。
    /// </summary>
    /// <param name="c"></param>
    /// <returns></returns>
    public static bool IsChinese(this char c)
    {
        return 0x4e00 <= c && c <= 0x9fbb;
    }

    /// <summary>
    /// 验证手机号。
    /// </summary>
    /// <param name="val"></param>
    /// <returns></returns>
    public static bool IsMobile(this string val)
    {
        if (string.IsNullOrWhiteSpace(val)) return false;

        return val.Length == 11 && val.StartsWith("1");
    }

    /// <summary>
    /// 验证当前字符串是否为 System.DateTime 类型。
    /// </summary>
    /// <param name="target">当前字符串。</param>
    /// <returns>如果 target 参数为 System.DateTime 类型，则为 true。</returns>
    public static bool IsDateTime(this string target)
    {
        if (string.IsNullOrWhiteSpace(target)) return false;
        return target.Length > 0 && Regex.IsMatch(target, @"^((\d{2}(([02468][048])|([13579][26]))[\-\/\s]((((0?[13578])|(1[02]))[\-\/\s]((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\-\/\s]((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\-\/\s]((0?[1-9])|([1-2][0-9])))))|(\d{2}(([02468][1235679])|([13579][01345789]))[\-\/\s]((((0?[13578])|(1[02]))[\-\/\s]((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\-\/\s]((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\-\/\s]((0?[1-9])|(1[0-9])|(2[0-8]))))))(\s(((0?[0-9])|(2[0-3])|(1[0-9]))\:([0-5]?[0-9])((\s)|(\:([0-5]?[0-9])))))?$");
    }

    /// <summary>
    /// 判断当前字符串是否在指定的范围内。
    /// </summary>
    /// <param name="target">当前字符串。</param>
    /// <param name="minLength">最小长度。</param>
    /// <param name="maxLength">最大长度。</param>
    /// <returns>在指定的范围内，则为 true。</returns>
    public static bool Range(this string target, int minLength, int maxLength) => !string.IsNullOrWhiteSpace(target) && target.Length >= minLength && target.Length <= maxLength;

    /// <summary>
    /// 对进当字符串进行哈希。
    /// </summary>
    /// <param name="target">当前字符串。</param>
    /// <returns>哈希值。</returns>
    public static string Hash(this string target)
    {
        using var md5 = System.Security.Cryptography.MD5.Create();

        var data = Encoding.Unicode.GetBytes(target);
        var hash = md5.ComputeHash(data);

        return Convert.ToBase64String(hash);
    }

    /// <summary>
    /// 指示 System.Text.RegularExpressions.Regex 构造函数中指定的正则表达式在指定的输入字符串中是否找到了匹配项。
    /// </summary>
    /// <param name="target">当前字符串。</param>
    /// <param name="expressionString">正则表达式。</param>
    /// <returns>验证通过，则为 True。</returns>
    public static bool IsMatch(this string target, string expressionString)
    {
        if (string.IsNullOrWhiteSpace(target)) return false;
        var expression = new Regex(expressionString);
        return expression.IsMatch(target);
    }

    /// <summary>
    /// 获取字符串真实长度。
    /// </summary>
    /// <remarks>1个汉字等于2个字符。</remarks>
    /// <param name="text">当前字符串。</param>
    /// <returns>字符串真实长度。</returns>
    public static int Length(this string text) => Encoding.UTF8.GetBytes(text).Length;

    /// <summary>
    /// 限制字符串长度，如果字符串的字符数超过 length，则忽略多出来的字符。
    /// </summary>
    /// <param name="text">原字符串。</param>
    /// <param name="length">限制字符串的字符数量。</param>
    /// <param name="tag">省略标记。</param>
    /// <returns>如果字符串的字符数没有超过 length 则直接返回原字符串，否则将超出的字符删除并加上省略标记返回。</returns>
    public static string LimitLength(this string text, int length, string tag)
    {
        if (text.IsNullOrWhiteSpace() || text.Length <= length)
        {
            return text;
        }

        return text[..length] + tag;
    }

    /// <summary>
    /// 隐藏字符串，必然显示最后一个字符。
    /// </summary>
    /// <param name="source"></param>
    /// <param name="tag">隐藏的符号。</param>
    /// <param name="number">符号个数。</param>
    /// <param name="startIndex">第几位开始隐藏。</param>
    /// <param name="hideNumber">隐藏的个数。（0：表示不使用）</param>
    /// <returns></returns>
    public static string GetHideString(this string source, string tag = "*", int number = 10, int startIndex = 1, int hideNumber = 0)
    {
        if (string.IsNullOrWhiteSpace(source))
            return source;

        if (startIndex < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(startIndex));
        }

        if (startIndex >= source.Length)
        {
            return source;
        }

        var result = source[..startIndex];

        if (number > 0)
        {
            for (int index = 0; index < number; index++)
            {
                result += tag;
            }
        }

        if (hideNumber > 0)
        {
            if (source.Length > startIndex + hideNumber)
            {
                //等效于：result += source.Substring(startIndex + hideNumber);
                result += source[(startIndex + hideNumber)..];
            }
        }
        else
        {
            if (source.Length > 2)
            {
                result += source.Substring(source.Length - 1, 1);
            }
        }

        return result;
    }

    /// <summary>
    /// 金额类型保留2位小数。
    /// </summary>
    /// <param name="source">原数字。</param>
    /// <returns>保留 2 位小数的金额。</returns>
    public static decimal GetMoney(this string source)
    {
        var status = decimal.TryParse(source, out decimal target);

        if (!status)
            throw new ArgumentException("当前类型不是 decimal", nameof(source));

        return target.GetMoney();
    }

    /// <summary>
    /// 截取字符串。
    /// </summary>
    /// <remarks>
    /// 从源字符串的 startIndex 开始，直到找到 tag 结束。
    /// </remarks>
    /// <param name="source">源字符串。</param>
    /// <param name="startIndex">开始位置。</param>
    /// <param name="tag">结束标签。</param>
    /// <returns></returns>
    public static string Substring(this string source, int startIndex, string tag)
    {
        if (string.IsNullOrWhiteSpace(source))
        {
            throw new ArgumentException("当前字符串不可以是空的", nameof(source));
        }

        if (startIndex < 0)
        {
            throw new ArgumentException("开始索引的值不正确", nameof(startIndex));
        }

        if (startIndex >= source.Length)
        {
            throw new ArgumentException("开始索引的值不正确", nameof(startIndex));
        }

        if (string.IsNullOrWhiteSpace(tag))
        {
            throw new ArgumentException("结束标签不可以是空的", nameof(tag));
        }

        //var current = source.Substring(startIndex);
        var current = source[startIndex..];

        var endIndex = current.IndexOf(tag);

        if (endIndex <= 0)
        {
            return string.Empty;
        }

        return source[..endIndex];
    }

    /// <summary>
    /// 单词首字母转换为大写。
    /// </summary>
    /// <remarks>
    /// 示例：“to title_case-TIME” 转换成“ToTitleCaseTime”。
    /// </remarks>
    /// <param name="source">源字符串。</param>
    /// <returns></returns>
    public static string ToTitleCase(this string source)
    {
        if (string.IsNullOrWhiteSpace(source))
        {
            return string.Empty;
        }

        source = source.Trim();

        return System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(source);
    }

    /// <summary>
    /// 将一个字符串转为驼峰命名法。
    /// </summary>
    /// <remarks>
    /// 示例：
    /// 1.源字符串“To_Field-Name”转换成“toFieldName”。
    /// 2.源字符串“ToFieldName”转换成“tofieldname”。
    /// </remarks>
    /// <param name="source">源字符串。</param>
    /// <returns></returns>
    public static string ToUpperCase(this string source)
    {
        var field = source ?? string.Empty;

        if (string.IsNullOrWhiteSpace(field))
        {
            return string.Empty;
        }
        else
        {
            if (field.Contains(' ') || field.Contains('_') || field.Contains('-'))
            {
                field = field.ToTitleCase();
            }

            if (field.Length <= 1)
            {
                return field.ToLower();
            }

            var first = field[..1].ToLower();
            var last = field[1..];

            return first + last;
        }
    }

    /// <summary>
    /// 截取指定文本中的一段内容。
    /// </summary>
    /// <param name="source">源文本。</param>
    /// <param name="lastNum">剩余的字符数。</param>
    /// <returns></returns>
    public static string SubstringLast(this string source, int lastNum)
    {
        if (source.Length < lastNum)
        {
            return string.Empty;
        }

        var startIndex = source.Length - lastNum;

        var content = source[startIndex..];

        if (!string.IsNullOrWhiteSpace(content))
        {
            return content;
        }

        return string.Empty;
    }

    /// <summary>
    /// 截取指定文本中的一段内容。
    /// </summary>
    /// <param name="source">源文本。</param>
    /// <param name="startTag">开始标记。</param>
    /// <param name="endTag">结束标记。</param>
    /// <returns>开始标记与结束标记范围内的内容。</returns>
    public static string Substring(this string source, string startTag, string endTag)
    {
        var content = source.Substring(startTag, endTag, true);

        if (!string.IsNullOrWhiteSpace(content))
        {
            return content;
        }

        return source;
    }

    /// <summary>
    /// 截取指定文本中的一段内容。
    /// </summary>
    /// <param name="source">源文本。</param>
    /// <param name="startTag">开始标记。</param>
    /// <param name="endTag">结束标记。</param>
    /// <param name="isTag">是否包含标记。</param>
    /// <returns>开始标记与结束标记范围内的内容。</returns>
    public static string Substring(this string source, string startTag, string endTag, bool isTag)
    {
        if (string.IsNullOrWhiteSpace(source))
            return string.Empty;

        if (isTag)
        {
            var startIndex = source.IndexOf(startTag, StringComparison.Ordinal);

            if (startIndex >= 0)
            {
                var content = source[startIndex..];

                var endIndex = content.IndexOf(endTag, StringComparison.Ordinal) + endTag.Length;

                if (endIndex >= 0)
                {
                    content = content[..endIndex];

                    return content;
                }
            }
        }
        else
        {
            var startIndex = source.IndexOf(startTag, StringComparison.Ordinal);

            if (startIndex >= 0)
            {
                startIndex += startTag.Length;

                var content = source[startIndex..];

                var endIndex = content.IndexOf(endTag, StringComparison.Ordinal);

                if (endIndex >= 0)
                {
                    content = content[..endIndex];

                    return content;
                }
            }
        }

        return string.Empty;
    }

    /// <summary>
    /// 将标记范围内的内容从指定文本中的删除。
    /// </summary>
    /// <param name="source">源文本。</param>
    /// <param name="startTag">开始标记。</param>
    /// <param name="endTag">结束标记。</param>
    /// <returns>开始标记与结束标记范围外的内容。</returns>
    public static string SubstringOther(this string source, string startTag, string endTag)
    {
        if (string.IsNullOrWhiteSpace(source))
            return string.Empty;

        var startIndex = source.IndexOf(startTag, StringComparison.Ordinal);
        var endIndex = source.IndexOf(endTag, StringComparison.Ordinal) + endTag.Length;

        var htmlBuilder = new StringBuilder();

        if (startIndex >= 0)
        {
            htmlBuilder.Append(source[..startIndex]);
        }

        if (startIndex >= 0 && endIndex > startIndex)
        {
            htmlBuilder.Append(source[endIndex..]);
        }

        if (htmlBuilder.Length > 0)
            return htmlBuilder.ToString();

        return source;
    }

    /// <summary>
    /// 在当前字符串（source）中，返回首个匹配指定字符串（target）的位置开始（不包含 target 字符串）至当前字符串（source）末尾的字符串。
    /// </summary>
    /// <remarks>
    /// 示例：
    /// var result = "123456".TrimStart("2");
    /// 返回结果(result)是：3456。
    /// 处理失败时，返回空字符串。
    /// </remarks>
    /// <param name="source">源字符串。</param>
    /// <param name="target">匹配的字符串。</param>
    /// <returns>首个匹配到 target 的位置开始至末尾所组成的新字符串。</returns>
    public static string TrimStart(this string source, string target)
    {
        if (string.IsNullOrWhiteSpace(target))
            return string.Empty;

        if (string.IsNullOrWhiteSpace(source))
            return string.Empty;

        var index = source.IndexOf(target.Trim());

        if (index < 0)
        {
            return string.Empty;
        }

        //return source.Substring(index + target.Length);
        return source[(index + target.Length)..];
    }

    /// <summary>
    /// 从当前 System.String 对象移除数组中指定的一组字符的所有尾部匹配项。
    /// </summary>
    /// <remarks>
    /// 处理失败时，返回空字符串。
    /// </remarks>
    /// <param name="source"></param>
    /// <param name="target"></param>
    /// <returns></returns>
    public static string TrimEnd(this string source, string target)
    {
        if (string.IsNullOrWhiteSpace(target))
            return string.Empty;

        if (string.IsNullOrWhiteSpace(source))
            return string.Empty;

        var index = source.LastIndexOf(target.Trim());

        if (index < 0)
        {
            return string.Empty;
        }

        return source[..index];
    }

    /// <summary>
    /// 清理字符串前后的空格与回车换行符等特殊符号。
    /// </summary>
    /// <param name="source">源字符串。</param>
    /// <returns>字符串。</returns>
    public static string TextTrim(this string source)
    {
        if (string.IsNullOrWhiteSpace(source))
        {
            return string.Empty;
        }

        return source.Trim().Trim('　').Trim('\r').Trim('\n').Trim();
    }

    /// <summary>
    /// 验证是否是纯数字。
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    public static bool IsCheckNumber(this string s)
    {
        return Regex.Match(s, "^\\d+$").Success;
    }

    /// <summary>
    /// 验证是不是内网IP。
    /// </summary>
    /// <param name="ipv4Address"></param>
    /// <returns></returns>
    public static bool IsPrivateNetwork(this string ipv4Address)
    {
        if (IPAddress.TryParse(ipv4Address, out var ip))
        {
            byte[] ipBytes = ip.GetAddressBytes();
            if (ipBytes[0] == 10) return true;
            if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31) return true;
            if (ipBytes[0] == 192 && ipBytes[1] == 168) return true;
        }

        return false;
    }

    /// <summary>
    /// 验证是不是公网IP。
    /// </summary>
    /// <param name="ipv4Address"></param>
    /// <returns></returns>
    public static bool IsPublicNetwork(this string ipv4Address)
    {
        if (string.IsNullOrWhiteSpace(ipv4Address))
            return false;

        if (!ipv4Address.Contains('.'))
            return false;

        if (ipv4Address.Contains("::ffff:"))
        {
            ipv4Address = ipv4Address.Replace("::ffff:", "");
        }

        return !ipv4Address.IsPrivateNetwork();
    }

    /// <summary>
    /// Json 转换成实体。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="json"></param>
    /// <returns></returns>
    public static T? FromJson<T>(this string json)
    {
        return JsonManager.Deserialize<T>(json);
    }
}
